package me.zhengjie.modules.system.service.impl

import me.zhengjie.common.utils.*
import me.zhengjie.common.utils.FileUtil.downloadExcel
import me.zhengjie.modules.system.domain.City
import me.zhengjie.modules.system.repository.AreaRepository
import me.zhengjie.modules.system.repository.CityRepository
import me.zhengjie.modules.system.service.CityService
import me.zhengjie.modules.system.service.dto.CityDTO
import me.zhengjie.modules.system.service.dto.CityQueryCriteria
import me.zhengjie.modules.system.service.mapper.CityMapper
import jakarta.persistence.criteria.CriteriaBuilder
import jakarta.persistence.criteria.CriteriaQuery
import jakarta.persistence.criteria.Root
import jakarta.servlet.http.HttpServletResponse
import org.springframework.cache.annotation.CacheConfig
import org.springframework.cache.annotation.CacheEvict
import org.springframework.cache.annotation.Cacheable
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.domain.Specification
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import java.io.IOException
import java.util.*
import java.util.function.Consumer

/**
 * @author kuki
 * @date 2022-07-21
 */
@Service
@CacheConfig(cacheNames = ["city"])
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = [Exception::class])
class CityServiceImpl(
    private val areaRepository: AreaRepository,
    private val cityRepository: CityRepository,
    private val cityMapper: CityMapper
) : CityService {
    @Cacheable(key = "'queryall_criteria_pageable'")
    override fun queryAll(criteria: CityQueryCriteria, pageable: Pageable?): Any? {
        val page =
            cityRepository.findAll(Specification{ root: Root<City>, criteriaQuery: CriteriaQuery<*>?, criteriaBuilder: CriteriaBuilder ->
                QueryHelp.getPredicate(
                    root,
                    criteria,
                    criteriaBuilder
                )
            }, pageable!!)
        return PageUtil.toPage(page.map { entity: City? -> cityMapper.toDto(entity) })
    }

    @Cacheable(key = "'queryall_criteria'")
    override fun queryAll(criteria: CityQueryCriteria): List<CityDTO> {
        val page= cityRepository.findAll (Specification{ root: Root<City>, criteriaQuery: CriteriaQuery<*>?, criteriaBuilder: CriteriaBuilder ->
            QueryHelp.getPredicate(
                root,
                criteria,
                criteriaBuilder
            )
        })
        return cityMapper.toDto(page) as List<CityDTO>
    }

    @Cacheable(key = "'findbyid'+#p0")
    override fun findById(id: Int): CityDTO? {
        val city = cityRepository.findById(id).orElseGet { City() }
        ValidationUtil.isNull(city!!.id, "City", "id", id)
        return cityMapper.toDto(city)
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = [Exception::class])
    override fun create(resources: City): CityDTO? {
        return cityMapper.toDto(cityRepository.save(resources))
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = [Exception::class])
    override fun update(resources: City) {
        val city = cityRepository.findById(resources.id!!).orElseGet { City() }
        ValidationUtil.isNull(city!!.id, "City", "id", resources.id)
        city.copy(resources)
        cityRepository.save(city)
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = [Exception::class])
    override fun delete(id: Int) {
        cityRepository.deleteById(id)
    }

    @Cacheable(key = "'getCityTree'")
    override fun getCityTree(citys: List<City?>): Any {
        val list: MutableList<Map<String, Any>> = LinkedList()
        citys.forEach(
            Consumer { it: City? ->
                if (it != null) {
                    val areaList = areaRepository.findByCityCode(it.cityCode!!)
                    val map: MutableMap<String, Any> = HashMap()
                    map["id"] = it.id!!
                    map["provinceCode"] = it.provinceCode!!
                    map["cityCode"] = it.cityCode
                    map["areaCode"] = it.areaCode!!
                    map["zipCode"] = it.cityCode
                    map["label"] = it.name!!
                    map["labelChs"] = it.nameChs!!
                    map["labelEn"] = it.nameEn!!
                    if (areaList != null && areaList.size != 0) {
                        map["children"] = areaList
                    }
                    list.add(map)
                }
            }
        )
        return list
    }

    @Throws(IOException::class)
    override fun download(all: List<CityDTO>, response: HttpServletResponse?) {
        val list: MutableList<Map<String?, Any?>?> = ArrayList()
        for (city in all) {
            val map: MutableMap<String?, Any?> = LinkedHashMap()
            map["中文名称"] = city.name!!
            map["英文名称"] = city.nameEn
            map["代码"] = city.cityCode
            map["省份"] = city.provinceCode
            list.add(map)
        }
        downloadExcel(list, response!!)
    }
}